import javascript

/** Gets a data flow node that represents an instance of `swagger-node`. */
DataFlow::Node swaggerInstance() {
  result = DataFlow::moduleImport("swagger-node-express")
  or
  result.getAPredecessor() = swaggerInstance()
  or
  result.(DataFlow::CallNode).getACallee().getAReturnedExpr() = swaggerInstance().asExpr()
  or
  result.(DataFlow::MethodCallNode).calls(swaggerInstance(), "createNew")
}

/** An Express route handler installed via `swagger-node`. */
class SwaggerRouteHandler extends Express::RouteHandler, DataFlow::FunctionNode {
  SwaggerRouteHandler() {
    exists(DataFlow::MethodCallNode addGet, DataFlow::ObjectLiteralNode resource |
      addGet.calls(swaggerInstance(), "addGet") and
      resource = addGet.getArgument(0).getALocalSource() and
      this = resource.getAPropertySource("action")
    )
  }

  override SimpleParameter getRouteHandlerParameter(string kind) {
    kind = "request" and result = getParameter(0).getParameter()
    or
    kind = "response" and result = getParameter(1).getParameter()
  }

  override HTTP::HeaderDefinition getAResponseHeader(string name) { none() }
}

/** Holds if `name` may be an alias for the `send` method on `res`. */
predicate sendMethodName(HTTP::Servers::ResponseSource res, string name) {
	name = "send"
	or
	exists (DataFlow::PropWrite pw |
	  res.flowsTo(pw.getBase()) and
	  pw.getPropertyName() = name and
	  sendMethodRef(pw.getRhs(), res)
	)
}

/** Holds if `pr` may be an access to the `send` method on `res`. */
predicate sendMethodRef(DataFlow::PropRead pr, HTTP::Servers::ResponseSource res) {
	res.flowsTo(pr.getBase()) and
	sendMethodName(res, pr.getPropertyName())
}

/** Recognize potentially aliased calls to `send`. */
class PotentiallyAliasedResponseSendArgument extends HTTP::ResponseSendArgument {
	HTTP::RouteHandler rh;

	PotentiallyAliasedResponseSendArgument() {
		exists (DataFlow::CallNode call, HTTP::Servers::ResponseSource res |
		  sendMethodRef(call.getCalleeNode(), res) and
		  rh = res.getRouteHandler() and
		  this = call.getArgument(0).asExpr()
		)
	}

	override HTTP::RouteHandler getRouteHandler() {
		result = rh
	}
}

from PotentiallyAliasedResponseSendArgument arg
select arg
